package com.common.utils;


import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/** JWT工具类：用于生成、校验Token，提取Token中的用户信息 */
public class JwtUtils {

  // Token过期时间：1天（单位：毫秒）
  public static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;

  // 请求头中Token的键名（统一常量，便于修改）
  public static final String TOKEN_HEADER = "Auth-Access-Token";

  // 设置秘钥
  public static final String SECRET = "Jwt-Token-Secret";

  /**
   * 刷新 Token（基于未过期的旧 Token 生成新 Token，延长有效期）
   *
   * @param oldToken 旧 Token（必须未过期且签名有效）
   * @param secret 密钥（需与生成旧 Token 时的密钥一致）
   * @return 新的 Token 字符串
   * @throws RuntimeException 若旧 Token 无效（过期、签名错误、无法提取用户信息），则抛出异常
   */
  public static String refreshToken(String oldToken, String secret) {
    // 1. 验证旧 Token 是否有效（未过期 + 签名正确）
    String username = getUserNameByToken(oldToken);
    if (StringUtils.isBlank(username)) {
      throw new RuntimeException("旧 Token 无效，无法提取用户名");
    }
    if (!verify(oldToken, username)) {
      throw new RuntimeException("旧 Token 签名验证失败，无法刷新");
    }
    if (isExpired(oldToken)) {
      throw new RuntimeException("旧 Token 已过期，无法刷新");
    }
    // 2. 基于旧 Token 中的用户名，生成新 Token（过期时间从当前时间重新计算）
    return createJWT(username, secret);
  }

  /**
   * 生成Token
   *
   * @param username 用户名（作为Token中的载荷信息）
   * @param secret 密钥（建议使用用户密码或系统统一密钥，用于签名Token）
   * @return 加密后的Token字符串
   */
  public static String createJWT(String username, String secret) {
    // 获取当前时间
    long nowTimeMills = System.currentTimeMillis();
    // 设置Token过期时间
    long expireDate = nowTimeMills + EXPIRE_TIME;
    // 使用HMAC256算法签名
    Algorithm algorithm = Algorithm.HMAC256(SECRET);
    // 设置头部
    Map<String, Object> headerMap = new HashMap<>();
    headerMap.put("typ", "JWT");
    headerMap.put("alg", "HS256");
    // 构建Token：包含用户名和过期时间
    return JWT.create()
        .withHeader(headerMap) // 设置头部信息
        .withClaim("userName", username) // 存储自定义载荷（用户名）
        .withIssuedAt(new Date(nowTimeMills)) // 设置签发时间
        .withExpiresAt(new Date(expireDate)) // 设置过期时间
        .sign(algorithm); // 签名生成Token
  }

  /**
   * 获取Token中的头部信息
   *
   * @param jwt
   * @return
   */
  public static String getHeaderFromJWT(String jwt) {
    return JWT.decode(jwt).getHeader();
  }

  /**
   * 解码Token中的头部信息
   *
   * @param headerBase64Url
   * @return
   */
  public static Map<String, Object> decodeHeader(String headerBase64Url) {
    try {
      // 1. Base64URL解码（Hutool的Base64工具自动支持Base64URL解码）
      byte[] headerBytes = Base64.decode(headerBase64Url);
      // 2. 转成UTF-8字符串（得到JSON格式）
      String headerJson = new String(headerBytes, StandardCharsets.UTF_8);
      // 3. 解析JSON为Map
      return JSON.parseObject(headerJson, new TypeReference<Map<String, Object>>() {});
    } catch (Exception e) {
      throw new RuntimeException("JWT header解码失败", e);
    }
  }

  /**
   * 获取Token中的载荷信息
   *
   * @param jwt
   * @return
   */
  public static Map<String, Claim> getClaimsFromJWT(String jwt) {
    return JWT.decode(jwt).getClaims();
  }

  /**
   * 校验Token是否有效
   *
   * @param jwt 待校验的Token
   * @param username 预期的用户名（用于匹配Token中的载荷）
   * @return true=有效，false=无效（包括过期、签名错误、用户名不匹配等）
   */
  public static boolean verify(String jwt, String username) {
    try {
      // 基于密钥创建验证器，并指定预期的用户名
      Algorithm algorithm = Algorithm.HMAC256(SECRET);
      JWTVerifier verifier = JWT.require(algorithm).withClaim("userName", username).build();
      // 验证Token（若无效会抛出异常）
      verifier.verify(jwt);
      return true;
    } catch (Exception e) {
      // 任何异常均视为Token无效
      return false;
    }
  }

  /**
   * 从Token中提取用户名（无需密钥，仅解码）
   *
   * @param token Token字符串
   * @return 用户名（若解码失败返回null）
   */
  public static String getUserNameByToken(String token) {
    try {
      DecodedJWT jwt = JWT.decode(token);
      return jwt.getClaim("userName").asString();
    } catch (JWTDecodeException e) {
      // 解码失败（如Token格式错误）
      return null;
    }
  }

  /**
   * 从请求头中获取Token，并提取用户名
   *
   * @param request HTTP请求对象
   * @return 用户名
   * @throws Exception 若Token不存在或无法提取用户名则抛出异常
   */
  public static String getUserName(HttpServletRequest request) throws Exception {
    // 从请求头获取Token
    String token = request.getHeader(TOKEN_HEADER);
    if (StringUtils.isEmpty(token)) {
      throw new Exception("请求头中未包含Token");
    }
    // 从Token提取用户名
    String userName = getUserNameByToken(token);
    if (StringUtils.isEmpty(userName)) {
      throw new Exception("Token中未获取到有效用户名");
    }
    return userName;
  }

  /**
   * 检查Token是否已过期
   *
   * @param token Token字符串
   * @return true=已过期，false=未过期
   */
  public static boolean isExpired(String token) {
    try {
      DecodedJWT jwt = JWT.decode(token);
      // 比较Token过期时间与当前时间
      return jwt.getExpiresAt().before(new Date());
    } catch (JWTDecodeException e) {
      // 解码失败视为过期（或无效）
      return true;
    }
  }

  public static void main(String[] args) {
    String token = createJWT("shuai", "123456");
    System.out.println("token : " + token);
    System.out.println("verify :" + verify(token, "shuai"));
    System.out.println("isExpired : " + isExpired(token));
    System.out.println("getUserAccount :" + getUserNameByToken(token));
    System.out.println("getHeaderFromJWT :" + getHeaderFromJWT(token));
    System.out.println("decodeHeader :" + decodeHeader(getHeaderFromJWT(token)));
    System.out.println("getClaimsFromJWT :" + getClaimsFromJWT(token));
  }
}
